#include <task.h>
#include "task_list.h"
#include "task_desc.h"
#include "task_wrapper.h"
#include <errno.h>
/* In order to obtain a cooperative behavior, we introduce the notion
 * of 'step'. A task step is everything the task do between two
 * cooperations. We then use one semaphore per task (see task_desc.h
 * file), that is used when starting the step of the task, and only
 * semaphore 'current_task_step_end' that the current task use to
 * indicate the end of its step.
 */

task_t current_task;
sem_t  current_task_step_end;

/********************
 * Task thread side *
 ********************/

/* When a task starts, it has stop immediatly and wait for its first
 * step to launched by the scheduler.
 */
void task_start (task_t task) {
  sem_wait (&task->step_begin);
}

/* When a task cooperates, it means that it has finished a step and
 * has to wait for its next step to be launched. We need to save the
 * value of current_task, since the scheduler may change it immediatly
 * after the 'sem_post'.
 */
void task_cooperate () {
  task_t task = current_task;
  sem_post (&current_task_step_end);
  sem_wait (&task->step_begin);
}

/* When a task finishes, it has finished a step and do not belong to
 * the list of tasks anymore. The task detroying is in the scheduler
 * main.
 */
void task_finish () {
  task_list_remove (current_task);
  current_task->state = TS_FINISHED;
  sem_post (&current_task_step_end);
}

/******************
 * Scheduler side *
 ******************/

/* The creation of a task only initialize its semaphore, its list
 * position and create a pthread on the task start routine. This
 * routine is wrapped in order to add the initial call to task_start and
 * final call to task_finish (see task_wrapper.*)
 */
int task_create (task_t *ptask, void *(*start_routine) (void *), void *arg) {
  task_t task = *ptask = (task_t) malloc (sizeof (struct _task_t));
  task->state  = TS_RUNNING;
  sem_init (&task->step_begin, 0, 0);
  sem_init (&task->msg_env, 0, 0);
  sem_init (&task->msg_rec, 0, 0);
  task_wrap_start_routine (task, &start_routine, &arg);
  pthread_create (&task->thread, NULL, start_routine, arg);
  task_list_add (task);
  return 0;
}

/* This function execute a step of the current task and returns "true"
 * if the task has more steps to execute, i.e. is not finished
 */
int task_step () {
  if (current_task->state == TS_RUNNING) {
    sem_post (&current_task->step_begin);
    sem_wait (&current_task_step_end);
  }
  return current_task->state != TS_FINISHED;
}


/* Self-explanatory */
void task_destroy () {
  sem_destroy (&current_task->step_begin);
  free (current_task);
}

/* Self-explanatory
 * Note that all the round robin is implemented by the task list
 * itself (files task_list.*) and that the 'for' iterate on the task list
 * that is constructed on-the-fly by the tasks themself.
 */
int main (int argc, char **argv) {
  int more_step;
  task_t next_task;

  sem_init (&current_task_step_end, 0, 0);

  for (task_create (&current_task, main_task, NULL);
       current_task != NULL;
       current_task = next_task) {


    more_step = task_step (current_task);
    next_task = task_list_next (current_task);
    if (!more_step) task_destroy ();

  }

  sem_destroy (&current_task_step_end);
  
  return 0;
}

void task_send (task_t * pt, char * mot, int taille)
{
	
	task_t t ;
	t=*pt;	
	t->mot_env = (char*) malloc (taille);
	
	errno=sem_trywait(&(t->msg_rec));
	if(errno != 0){
		task_cooperate();
		sem_wait(&(t->msg_rec));	
	}
	memcpy(t->mot_env,mot,taille);
	sem_post(&t->msg_env);
	task_cooperate();

}

void task_receive (char * mot, int taille)
{
	
	task_t t ;
	t=current_task;
	
	errno=sem_trywait(&(t->msg_env));
	if(errno != 0){
		sem_post(&t->msg_rec);
		task_cooperate();
		
		sem_wait(&(t->msg_env));
			
	}
	memcpy(mot,t->mot_env,taille);
	
}


void task_ns_register (task_t *servor, char* mot)
{
	char enr[] = "SAVE_ME";
	int i=0;
	int taille_mot = sizeof(enr);
	
	task_send (servor,&taille_mot,sizeof(int));

	task_send (servor,enr,taille_mot);
	while(mot[i]!=NULL)i++;
	taille_mot = i+1;	

	task_send (servor,&taille_mot,sizeof(int));

	task_send (servor,mot,taille_mot);

}
